// SPDX-FileCopyrightText: 2024 CitruS Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <common/fs/fs.h>
#include <common/fs/path_util.h>
#include <common/settings.h>
#include <hid_core/hid_types.h>

#include "android_config.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "input_common/drivers/virtual_gamepad.h"
#include "input_common/drivers/android.h"
#include "native.h"
#include "binder.h"

std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;

bool IsHandheldOnly() {
    const auto npad_style_set =
        EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag();

    if (npad_style_set.fullkey == 1) {
        return false;
    }

    if (npad_style_set.handheld == 0) {
        return false;
    }

    return !Settings::IsDockedMode();
}

std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
    return filename.replace_extension();
}

bool IsProfileNameValid(std::string_view profile_name) {
    return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
}

bool ProfileExistsInFilesystem(std::string_view profile_name) {
    return Common::FS::Exists(Common::FS::GetCitrusPath(Common::FS::CitrusPath::ConfigDir) / "input" /
                              fmt::format("{}.ini", profile_name));
}

bool ProfileExistsInMap(const std::string& profile_name) {
    return map_profiles.find(profile_name) != map_profiles.end();
}

bool SaveProfile(const std::string& profile_name, std::size_t player_index) {
    if (!ProfileExistsInMap(profile_name)) {
        return false;
    }

    Settings::values.players.GetValue()[player_index].profile_name = profile_name;
    map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index);
    return true;
}

bool LoadProfile(const std::string& profile_name, std::size_t player_index) {
    if (!ProfileExistsInMap(profile_name)) {
        return false;
    }

    if (!ProfileExistsInFilesystem(profile_name)) {
        map_profiles.erase(profile_name);
        return false;
    }

    LOG_INFO(Config, "Loading input profile `{}`", profile_name);

    Settings::values.players.GetValue()[player_index].profile_name = profile_name;
    map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index);
    return true;
}

void ApplyControllerConfig(size_t player_index,
                           const std::function<void(Core::HID::EmulatedController*)>& apply) {
    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    if (player_index == 0) {
        auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
        auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
        handheld->EnableConfiguration();
        player_one->EnableConfiguration();
        apply(handheld);
        apply(player_one);
        handheld->DisableConfiguration();
        player_one->DisableConfiguration();
        handheld->SaveCurrentConfig();
        player_one->SaveCurrentConfig();
    } else {
        auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
        controller->EnableConfiguration();
        apply(controller);
        controller->DisableConfiguration();
        controller->SaveCurrentConfig();
    }
}

std::vector<s32> GetSupportedStyles(int player_index) {
    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    const auto npad_style_set = hid_core.GetSupportedStyleTag();
    std::vector<s32> supported_indexes;
    if (npad_style_set.fullkey == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
    }

    if (npad_style_set.joycon_dual == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
    }

    if (npad_style_set.joycon_left == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
    }

    if (npad_style_set.joycon_right == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
    }

    if (player_index == 0 && npad_style_set.handheld == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
    }

    if (npad_style_set.gamecube == 1) {
        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
    }

    return supported_indexes;
}

void ConnectController(int player_index, bool connected) {
    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
        auto supported_styles = GetSupportedStyles(player_index);
        auto controller_style = controller->GetNpadStyleIndex(true);
        auto style = std::find(supported_styles.begin(), supported_styles.end(),
                               static_cast<int>(controller_style));
        if (style == supported_styles.end() && !supported_styles.empty()) {
            controller->SetNpadStyleIndex(
                static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
        }
    });

    if (player_index == 0) {
        auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
        auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
        handheld->EnableConfiguration();
        player_one->EnableConfiguration();
        if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
            if (connected) {
                handheld->Connect();
            } else {
                handheld->Disconnect();
            }
            player_one->Disconnect();
        } else {
            if (connected) {
                player_one->Connect();
            } else {
                player_one->Disconnect();
            }
            handheld->Disconnect();
        }
        handheld->DisableConfiguration();
        player_one->DisableConfiguration();
        handheld->SaveCurrentConfig();
        player_one->SaveCurrentConfig();
    } else {
        auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
        controller->EnableConfiguration();
        if (connected) {
            controller->Connect();
        } else {
            controller->Disconnect();
        }
        controller->DisableConfiguration();
        controller->SaveCurrentConfig();
    }
}

::ndk::ScopedAStatus Binder::NativeInput_isHandheldOnly(bool* _aidl_return) {
    *_aidl_return = IsHandheldOnly();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onGamePadButtonEvent(const std::string& guid, int32_t port, int32_t buttonId, int32_t action) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState(guid, port, buttonId, action);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onGamePadAxisEvent(const std::string& guid, int32_t port, int32_t axis, float value) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition(guid, port, axis, value);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onGamePadMotionEvent(const std::string& guid, int32_t port, int64_t deltaTimestamp, float xGyro, float yGyro, float zGyro, float xAccel, float yAccel, float zAccel) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState(
                guid, port, deltaTimestamp, xGyro, yGyro,
                zGyro, xAccel, yAccel, zAccel);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onReadNfcTag(const std::vector<uint8_t>& in_data) {
    const std::span<const u8> data { in_data };

    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onRemoveNfcTag() {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo();
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onTouchPressed(int32_t fingerId, float xAxis, float yAxis) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().Window().OnTouchPressed(fingerId, xAxis, yAxis);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onTouchMoved(int32_t fingerId, float xAxis, float yAxis) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().Window().OnTouchMoved(fingerId, xAxis, yAxis);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onTouchReleased(int32_t fingerId) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().Window().OnTouchReleased(fingerId);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onOverlayButtonEventImpl(int32_t port, int32_t buttonId, int32_t action) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState(
            port, buttonId, action == 1);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onOverlayJoystickEventImpl(int32_t port, int32_t stickId, float xAxis, float yAxis) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition(
            port, stickId, xAxis, yAxis);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_onDeviceMotionEvent(int32_t port, int64_t deltaTimestamp, float xGyro, float yGyro, float zGyro, float xAccel, float yAccel, float zAccel) {
    if (EmulationSession::GetInstance().IsRunning()) {
        EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState(
            port, deltaTimestamp, xGyro, yGyro, zGyro, xAccel, yAccel,
            zAccel);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_reloadInputDevices() {
    EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getInputDevices(std::vector<std::string>* _aidl_return) {
    auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices();
    *_aidl_return = std::vector<std::string>(devices.size());
    for (size_t i = 0; i < devices.size(); ++i) {
        (*_aidl_return)[i] = devices[i].Serialize();
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_loadInputProfiles() {
        map_profiles.clear();
    const auto input_profile_loc =
        Common::FS::GetCitrusPath(Common::FS::CitrusPath::ConfigDir) / "input";

    if (Common::FS::IsDir(input_profile_loc)) {
        Common::FS::IterateDirEntries(
            input_profile_loc,
            [&](const std::filesystem::path& full_path) {
                const auto filename = full_path.filename();
                const auto name_without_ext =
                    Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));

                if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) {
                    map_profiles.insert_or_assign(
                        name_without_ext, std::make_unique<AndroidConfig>(
                                              name_without_ext, Config::ConfigType::InputProfile));
                }

                return true;
            },
            Common::FS::DirEntryFilter::File);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getInputProfileNames(std::vector<std::string>* _aidl_return) {
    (*_aidl_return) = std::vector<std::string>();
    auto it = map_profiles.cbegin();
    while (it != map_profiles.cend()) {
        const auto& [profile_name, config] = *it;
        if (!ProfileExistsInFilesystem(profile_name)) {
            it = map_profiles.erase(it);
            continue;
        }

        (*_aidl_return).push_back(profile_name);
        ++it;
    }

    std::stable_sort((*_aidl_return).begin(), (*_aidl_return).end());
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_isProfileNameValid(const std::string& name, bool* _aidl_return) {
    *_aidl_return = name.find_first_of("<>:;\"/\\|,.!?*") ==
           std::string::npos;
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_createProfile(const std::string& name, int32_t playerIndex, bool* _aidl_return) {
    if (ProfileExistsInMap(name)) {
        *_aidl_return = false;
        return ::ndk::ScopedAStatus::ok();
    }

    map_profiles.insert_or_assign(
        name,
        std::make_unique<AndroidConfig>(name, Config::ConfigType::InputProfile));

    *_aidl_return = SaveProfile(name, playerIndex);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_deleteProfile(const std::string& name, int32_t playerIndex, bool* _aidl_return) {
    if (!ProfileExistsInMap(name)) {
        *_aidl_return = false;
        return ::ndk::ScopedAStatus::ok();
    }

    if (!ProfileExistsInFilesystem(name) ||
        Common::FS::RemoveFile(map_profiles[name]->GetConfigFilePath())) {
        map_profiles.erase(name);
    }

    Settings::values.players.GetValue()[playerIndex].profile_name = "";
    *_aidl_return = !ProfileExistsInMap(name) && !ProfileExistsInFilesystem(name);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_loadProfile(const std::string& name, int32_t playerIndex, bool* _aidl_return) {
    *_aidl_return = LoadProfile(name, playerIndex);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_saveProfile(const std::string& name, int32_t playerIndex, bool* _aidl_return) {
    *_aidl_return = SaveProfile(name, playerIndex);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_loadPerGameConfiguration(int32_t playerIndex, int32_t selectedIndex, const std::string& profile_name) {
    static constexpr size_t HANDHELD_INDEX = 8;

    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    Settings::values.players.SetGlobal(false);

    auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(playerIndex);

    if (selectedIndex == 0) {
        Settings::values.players.GetValue()[playerIndex].profile_name = "";
        if (playerIndex == 0) {
            Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
        }
        Settings::values.players.SetGlobal(true);
        emulated_controller->ReloadFromSettings();
        return ::ndk::ScopedAStatus::ok();
    }
    if (profile_name.empty()) {
        return ::ndk::ScopedAStatus::ok();
    }
    auto& player = Settings::values.players.GetValue()[playerIndex];
    auto& global_player = Settings::values.players.GetValue(true)[playerIndex];
    player.profile_name = profile_name;
    global_player.profile_name = profile_name;
    // Read from the profile into the custom player settings
    LoadProfile(profile_name, playerIndex);
    // Make sure the controller is connected
    player.connected = true;

    emulated_controller->ReloadFromSettings();

    if (playerIndex > 0) {
        return ::ndk::ScopedAStatus::ok();
    }
    // Handle Handheld cases
    auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
    auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
    if (player.controller_type == Settings::ControllerType::Handheld) {
        handheld_player = player;
    } else {
        handheld_player = {};
    }
    handheld_controller->ReloadFromSettings();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_beginMapping(int32_t type) {
    EmulationSession::GetInstance().GetInputSubsystem().BeginMapping(
        static_cast<InputCommon::Polling::InputType>(type));
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getNextInput(std::string* _aidl_return) {
    *_aidl_return = EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_stopMapping() {
    EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_updateMappingsWithDefaultImpl(int32_t playerIndex, const std::string& deviceParams, const std::string& displayName) {
    auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem();

    // Clear all previous mappings
    for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetButtonParam(button_id, {});
        });
    }
    for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetStickParam(analog_id, {});
        });
    }

    // Apply new mappings
    auto device = Common::ParamPackage(deviceParams);
    auto button_mappings = input_subsystem.GetButtonMappingForDevice(device);
    auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device);
    for (const auto& button_mapping : button_mappings) {
        const std::size_t index = button_mapping.first;
        auto named_mapping = button_mapping.second;
        named_mapping.Set("display", displayName);
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetButtonParam(index, named_mapping);
        });
    }
    for (const auto& analog_mapping : analog_mappings) {
        const std::size_t index = analog_mapping.first;
        auto named_mapping = analog_mapping.second;
        named_mapping.Set("display", displayName);
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetStickParam(index, named_mapping);
        });
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getButtonParamImpl(int32_t playerIndex, int32_t buttonId, std::string* _aidl_return) {
    *_aidl_return = EmulationSession::GetInstance()
                                               .System()
                                               .HIDCore()
                                               .GetEmulatedControllerByIndex(playerIndex)
                                               ->GetButtonParam(buttonId)
                                               .Serialize();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_setButtonParamImpl(int32_t playerIndex, int32_t buttonId, const std::string& param) {
    ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
        controller->SetButtonParam(buttonId,
                                   Common::ParamPackage(param));
    });
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getStickParamImpl(int32_t playerIndex, int32_t stickId, std::string* _aidl_return) {
    *_aidl_return = EmulationSession::GetInstance()
                                               .System()
                                               .HIDCore()
                                               .GetEmulatedControllerByIndex(playerIndex)
                                               ->GetStickParam(stickId)
                                               .Serialize();
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_setStickParamImpl(int32_t playerIndex, int32_t stickId, const std::string& param) {
    ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
        controller->SetStickParam(stickId,
                                  Common::ParamPackage(param));
    });
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getButtonNameImpl(const std::string& param, int32_t* _aidl_return) {
    *_aidl_return = static_cast<int32_t>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName(
        Common::ParamPackage(param)));
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getSupportedStyleTagsImpl(int32_t playerIndex, std::vector<int32_t>* _aidl_return) {
    *_aidl_return = GetSupportedStyles(playerIndex);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getStyleIndexImpl(int32_t playerIndex, int32_t* _aidl_return) {
    *_aidl_return = static_cast<s32>(EmulationSession::GetInstance()
                                .System()
                                .HIDCore()
                                .GetEmulatedControllerByIndex(playerIndex)
                                ->GetNpadStyleIndex(true));
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_setStyleIndexImpl(int32_t playerIndex, int32_t styleIndex) {
    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    auto type = static_cast<Core::HID::NpadStyleIndex>(styleIndex);
    ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
        controller->SetNpadStyleIndex(type);
    });
    if (playerIndex == 0) {
        auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
        auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
        ConnectController(playerIndex,
                          player_one->IsConnected(true) || handheld->IsConnected(true));
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_isControllerImpl(const std::string& params, bool* _aidl_return) {
    *_aidl_return = static_cast<bool>(EmulationSession::GetInstance().GetInputSubsystem().IsController(
        Common::ParamPackage(params)));
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_getIsConnected(int32_t playerIndex, bool* _aidl_return) {
    auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
    auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(playerIndex));
    if (playerIndex == 0 &&
        controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
        *_aidl_return = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true);
    }
    else *_aidl_return = controller->IsConnected(true);
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_connectControllersImpl(const std::vector<bool>& connected) {
    for (size_t i = 0; i < connected.size(); ++i) {
        ConnectController(i, connected[i]);
    }
    return ::ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus Binder::NativeInput_resetControllerMappings(int32_t playerIndex) {
    for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetButtonParam(button_id, {});
        });
    }
    for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
        ApplyControllerConfig(playerIndex, [&](Core::HID::EmulatedController* controller) {
            controller->SetStickParam(analog_id, {});
        });
    }
    return ::ndk::ScopedAStatus::ok();
}
